#ifndef __KERNEL_DEV_BLOCK_H__
#define __KERNEL_DEV_BLOCK_H__

#include "std/list.h"
#include "std/avltree.h"
#include "kernel/slab.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct dev_blk_data
{
	unsigned long long blkid;
	uint blkcnt : 31;
	uint use_page_list : 1;
	union {
		unsigned char* ptr;
		listnode_t* page_list;
	} buf;
}
dev_blk_data_t;

#define DEV_BLK_NODE_FLAG_ACCESS_READ			(1)
#define DEV_BLK_NODE_FLAG_ACCESS_WRITE			(2)
#define DEV_BLK_NODE_FLAG_SUCCESS_NOTIFIED		(3)

typedef struct dev_blk_req_node
{
	// if the node is in the request list
	// the avlnode is used
	// otherwise, ownerlist is used
	union {
		avl_node_t avlnode;
		listnode_t ownerlist;
	} u;

	uint flags;
	dev_blk_data_t data;
}
dev_blk_req_node_t;

#define DEV_BLK_REQLST_TYPE_FIFO		(1)
#define DEV_BLK_REQLST_TYPE_ELEVATOR	(2)
#define DEV_BLK_REQLST_RUNNING			(4)

// for "one round" case, the "drain" flag will be set
// once all items has been submitted
#define DEV_BLK_REQLST_DRAIN			(8)

// block request list
typedef struct dev_blk_reqlist
{
	// this is used to link to another list, to help to
	// implement something like elevator algorithm
	listnode_t	ownerlist;

	// this is the real request list
	// two types could be selected
	// 1) FIFO
	// 2) Elevator
	union {
		listnode_t fifo;
		avl_node_t* elevator;
	} h;

	// timestamp for first request and last request
	unsigned int ts_fstreq, ts_lstreq;

	// remaining blocks to be handled
	unsigned int remaining;

	// the block size for this request list
	unsigned int block_size;

	// flags
	unsigned int flags;
}
dev_blk_reqlist_t;

// errors
#define DEV_BLK_REQ_ERR_UNKNOWN_ACCESS_TYPE		(1)
#define DEV_BLK_REQ_ERR_BAD_ALIGNMENT			(2)
#define DEV_BLK_REQ_ERR_BAD_PARAMETER			(3)
#define DEV_BLK_REQ_BLOCK_TOO_LARGE				(4)
#define DEV_BLK_REQ_ERR_NO_MEMORY				(5)
#define DEV_BLK_REQ_ERR_ELEVATOR_NEXT_ROUND		(6)

// access type
#define DEV_BLK_ACCESS_READ			(1)
#define DEV_BLK_ACCESS_WRITE		(2)
#define DEV_BLK_BUF_PHY_PAGE_LIST	(4)

// buffer type
// if this is not specified, we regard this
// as the mapped memory
#define DEV_BLK_BUFTYPE_PHY_PAGE	(4)

// global allocator
extern kmem_cache_t* dev_blk_req_cache;

// external functions
// this is to allocate a new request list
dev_blk_reqlist_t* dev_reqlist_alloc(uint maxblks, uint type, uint block_size);

// releas a request list
bool dev_reqlist_release(dev_blk_reqlist_t* reqlist);

// create a request object
int dev_block_new_request(uint flags, uint blksz,
		void* buffer, unsigned long long blkid, uint blks, dev_blk_req_node_t** req);

// release a request object
#define dev_block_release_request(req)	kmem_cache_free(req)

// submit a request object
int dev_block_submit_request(dev_blk_reqlist_t* rlist, dev_blk_req_node_t* req);

#ifdef __cplusplus
}
#endif
#endif
/* EOF */
